/*-------------------------------------------------------------------------
 *
 * pg_collation.c
 *	  支持pg_collation关系操作的常规例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/catalog/pg_collation.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/genam.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_namespace.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/pg_locale.h"
#include "utils/rel.h"
#include "utils/syscache.h"


 /*
 * CollationCreate
 *
 * 向 pg_collation 添加一个新元组。
 *
 * if_not_exists：如果为真，则在名称重复时不失败，仅打印通知
 * 并返回 InvalidOid。
 * quiet：如果为真，则在名称重复时不失败，仅静默返回
 * InvalidOid（覆盖 if_not_exists）。
 */
Oid CollationCreate(const char *fc_collname, Oid fc_collnamespace,
				Oid fc_collowner,
				char fc_collprovider,
				bool fc_collisdeterministic,
				int32 fc_collencoding,
				const char *fc_collcollate, const char *fc_collctype,
				const char *fc_colliculocale,
				const char *fc_collversion,
				bool fc_if_not_exists,
				bool fc_quiet)
{
	Relation	fc_rel;
	TupleDesc	fc_tupDesc;
	HeapTuple	fc_tup;
	Datum		fc_values[Natts_pg_collation];
	bool		fc_nulls[Natts_pg_collation];
	NameData	fc_name_name;
	Oid			fc_oid;
	ObjectAddress fc_myself,
				fc_referenced;

	AssertArg(fc_collname);
	AssertArg(fc_collnamespace);
	AssertArg(fc_collowner);
	AssertArg((fc_collcollate && fc_collctype) || fc_colliculocale);

	 /*
	 * 确保没有同名和编码的现有排序规则。
	 *
	 * 这无论如何会被唯一索引捕获；我们只是在提供一个
	 * 更友好的错误消息。唯一索引提供了对
	 * 竞争条件的保护。
	 */
	fc_oid = GetSysCacheOid3(COLLNAMEENCNSP,
						  Anum_pg_collation_oid,
						  PointerGetDatum(fc_collname),
						  Int32GetDatum(fc_collencoding),
						  ObjectIdGetDatum(fc_collnamespace));
	if (OidIsValid(fc_oid))
	{
		if (fc_quiet)
			return InvalidOid;
		else if (fc_if_not_exists)
		{
			/*
			 * 如果我们处于扩展脚本中，必须确保预先存在的
			 * 对象是扩展的成员，以避免安全风险。
			 */
			ObjectAddressSet(fc_myself, CollationRelationId, fc_oid);
			checkMembershipInCurrentExtension(&fc_myself);

			/* 可以跳过 */
			ereport(NOTICE,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 fc_collencoding == -1
					 ? errmsg("collation \"%s\" already exists, skipping",
							  fc_collname)
					 : errmsg("collation \"%s\" for encoding \"%s\" already exists, skipping",
							  fc_collname, pg_encoding_to_char(fc_collencoding))));
			return InvalidOid;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 fc_collencoding == -1
					 ? errmsg("collation \"%s\" already exists",
							  fc_collname)
					 : errmsg("collation \"%s\" for encoding \"%s\" already exists",
							  fc_collname, pg_encoding_to_char(fc_collencoding))));
	}

	 /* 打开 pg_collation；有关锁定级别的请参见下文 */
	fc_rel = table_open(CollationRelationId, ShareRowExclusiveLock);

	 /*
	 * 还禁止特定编码排序规则覆盖任何编码
	 * 排序规则，或任何编码排序规则被覆盖（见
	 * get_collation_name()）。该测试没有被唯一索引支持，
	 * 因此我们提前获取 ShareRowExclusiveLock，以防止
	 * 并发更改误导此检查。
	 */
	if (fc_collencoding == -1)
		fc_oid = GetSysCacheOid3(COLLNAMEENCNSP,
							  Anum_pg_collation_oid,
							  PointerGetDatum(fc_collname),
							  Int32GetDatum(GetDatabaseEncoding()),
							  ObjectIdGetDatum(fc_collnamespace));
	else
		fc_oid = GetSysCacheOid3(COLLNAMEENCNSP,
							  Anum_pg_collation_oid,
							  PointerGetDatum(fc_collname),
							  Int32GetDatum(-1),
							  ObjectIdGetDatum(fc_collnamespace));
	if (OidIsValid(fc_oid))
	{
		if (fc_quiet)
		{
			table_close(fc_rel, NoLock);
			return InvalidOid;
		}
		else if (fc_if_not_exists)
		{
			/*
			 * 如果我们处于扩展脚本中，必须确保预先存在的
			 * 对象是扩展的成员，以避免安全风险。
			 */
			ObjectAddressSet(fc_myself, CollationRelationId, fc_oid);
			checkMembershipInCurrentExtension(&fc_myself);

			/* 可以跳过 */
			table_close(fc_rel, NoLock);
			ereport(NOTICE,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("collation \"%s\" already exists, skipping",
							fc_collname)));
			return InvalidOid;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("collation \"%s\" already exists",
							fc_collname)));
	}

	fc_tupDesc = RelationGetDescr(fc_rel);

	 /* 形成一个元组 */
	memset(fc_nulls, 0, sizeof(fc_nulls));

	namestrcpy(&fc_name_name, fc_collname);
	fc_oid = GetNewOidWithIndex(fc_rel, CollationOidIndexId,
							 Anum_pg_collation_oid);
	fc_values[Anum_pg_collation_oid - 1] = ObjectIdGetDatum(fc_oid);
	fc_values[Anum_pg_collation_collname - 1] = NameGetDatum(&fc_name_name);
	fc_values[Anum_pg_collation_collnamespace - 1] = ObjectIdGetDatum(fc_collnamespace);
	fc_values[Anum_pg_collation_collowner - 1] = ObjectIdGetDatum(fc_collowner);
	fc_values[Anum_pg_collation_collprovider - 1] = CharGetDatum(fc_collprovider);
	fc_values[Anum_pg_collation_collisdeterministic - 1] = BoolGetDatum(fc_collisdeterministic);
	fc_values[Anum_pg_collation_collencoding - 1] = Int32GetDatum(fc_collencoding);
	if (fc_collcollate)
		fc_values[Anum_pg_collation_collcollate - 1] = CStringGetTextDatum(fc_collcollate);
	else
		fc_nulls[Anum_pg_collation_collcollate - 1] = true;
	if (fc_collctype)
		fc_values[Anum_pg_collation_collctype - 1] = CStringGetTextDatum(fc_collctype);
	else
		fc_nulls[Anum_pg_collation_collctype - 1] = true;
	if (fc_colliculocale)
		fc_values[Anum_pg_collation_colliculocale - 1] = CStringGetTextDatum(fc_colliculocale);
	else
		fc_nulls[Anum_pg_collation_colliculocale - 1] = true;
	if (fc_collversion)
		fc_values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(fc_collversion);
	else
		fc_nulls[Anum_pg_collation_collversion - 1] = true;

	fc_tup = heap_form_tuple(fc_tupDesc, fc_values, fc_nulls);

	 /* 插入一个新元组 */
	CatalogTupleInsert(fc_rel, fc_tup);
	Assert(OidIsValid(fc_oid));

	 /* 为新排序规则设置依赖 */
	fc_myself.classId = CollationRelationId;
	fc_myself.objectId = fc_oid;
	fc_myself.objectSubId = 0;

	 /* 创建对命名空间的依赖 */
	fc_referenced.classId = NamespaceRelationId;
	fc_referenced.objectId = fc_collnamespace;
	fc_referenced.objectSubId = 0;
	recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_NORMAL);

	 /* 创建对所有者的依赖 */
	recordDependencyOnOwner(CollationRelationId, fc_oid, fc_collowner);

	/* 对扩展的依赖 */
	recordDependencyOnCurrentExtension(&fc_myself, false);

	 /* 新排序规则的后创建钩子 */
	InvokeObjectPostCreateHook(CollationRelationId, fc_oid, 0);

	heap_freetuple(fc_tup);
	table_close(fc_rel, NoLock);

	return fc_oid;
}
